Skip to content

员工管理

1.1 新增员工

1.1.1 需求分析和设计

image-20260310155609220

有以下要求:

  • 账号必须是唯一的
  • 手机号为合法的11位手机号码
  • 身份证号位合法的18位身份证号码
  • 密码默认为123456,后续可自行修改

1.1.2 接口设计

image-20260310155954403

1.1.3 数据库设计

image-20260310160157244

1.1.4 代码开发

  • 当前端提交的数据和实体类中对应的属性差别比较大时,建议使用DTO来封装数据

  • 使用DTO时,其属性需要补充时,将其映射到原本的对象,可通过工具类实现:

    java
    // 对象属性拷贝
    BeanUtils.copyProperties(employeeDTO,employee);

1.1.5 代码完善

目前新增员工模块存在两个问题:

  • 违反数据库唯一约束异常未处理
    • 处理方法:捕捉特定异常,在全局异常处理器添加处理逻辑
  • 新增员工的两个字段(创建人和修改人id)需要查询当前登录用户信息
    • 处理方法:解析JWT令牌的token,获得登录用户的用户信息(因为在生成token的时候,将用户id放进去了,所以可以解析令牌,获取到cliams)

      image-20260311164830989

      • 新问题:得到用户信息后,如何将信息传递给service?
        • 解决方法:有一个线程空间的概念,ThreadLocal,是Thread的局部变量,为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问
        • 客户端发起的每一次请求都对应一次单独的线程,可以将获取到的用户id存储到ThreadLocal里面,service再去拿到该信息即可
        • ThreadLocal常用方法:
          • public void set(T value) 设置当前线程的线程局部变量的值
          • public T get() 返回当前线程局部变量的值
          • public void remove() 移除当前线程的线程局部变量的值

1.2 员工分页查询

  1. 使用第三方插件 PageHelper,用法:

    java
    /**
     * 分页查询
     */
    @Override
    public PageResult page(EmployeePageQueryDTO employeePageQueryDTO) {
        // 设置分页参数
        PageHelper.startPage(employeePageQueryDTO.getPage(),employeePageQueryDTO.getPageSize());
        // 执行分页查询,返回 Page<Employee> 对象
        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);
        return new PageResult(page.getTotal(),page.getResult());
    }
  2. 问题:后端返回给前端的日期数据格式不正确

    image-20260311201655793

    时间字段为数组格式,原因是SpringBoot默认使用的JSON序列化工具Jackson处理LocalDateTime类型时,默认将其转化为了数组格式

  3. 解决方法:

    • 在属性上加入注解,对日期进行格式化,缺点是一个一个属性配置太麻烦

      java
      @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
      private LocalDateTime createTime;
    • 继承 WebMvcConfigurationSupport 类或实现 WebMvcConfigurer 接口,重写 extendMessageConverters 方法,扩展SpringMVC的消息转换器,统一对日期类型进行格式化处理,消息转换器使用对象映射器的规则,对Java对象和JSON数据之间进行序列化和反序列化。通俗讲,就是我们自己设计一套规则,后端返回数据时会应用这套规则。

      代码实现:

      • 消息转换器:
      java
      @Configuration
      public class WebMvcConfiguration extends WebMvcConfigurationSupport {
      
          /**
           * 扩展消息转换器
           * @param converters
           */
          @Override
          protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
              // 1. 创建一个新的消息转换器对象
              MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
      
              // 2. 为对象转换器设置一个自定义的对象映射器(Jackson 的核心)
              // 这里推荐使用一个自定义的 JacksonObjectMapper (见下文)
              converter.setObjectMapper(new JacksonObjectMapper());
      
              // 3. 将自己的转换器容器放到最前面,让它优先执行
              converters.add(0, converter);
          }
      }
      • 对象映射器
      java
      /**
       * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
       * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
       * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
       */
      public class JacksonObjectMapper extends ObjectMapper {
      
          public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
          //public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
          public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
          public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
      
          public JacksonObjectMapper() {
              super();
              //收到未知属性时不报异常
              this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
      
              //反序列化时,属性不存在的兼容处理
              this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
      
              SimpleModule simpleModule = new SimpleModule()
                      .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                      .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                      .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
                      .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                      .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                      .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
      
              //注册功能模块 例如,可以添加自定义序列化器和反序列化器
              this.registerModule(simpleModule);
          }
      }

1.3 启用禁用员工账号

1.3.1 需求分析和设计

image-20260311210312804

1.4 编辑员工

需要两个接口:

  • 根据ID查询用户
    • 注意:对于返回前端的数据,我们保持”最小化“原则,即只传给前端需要的数据,这里我们将传给前端的数据中的用户的密码置空
  • 修改用户信息